using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using OhioState.AssetManaged;
using OhioState.Math;


namespace OhioState.Graphics.Proxy
{
    /// <summary>
    /// This is a struct to hold uniform variable information such as array size, type, and location
    /// </summary>
    [Serializable()]
    public class UniformVariableInfo
    {
        /// <summary>
        /// Enumerates all the possible uniform types
        /// </summary>
        public enum UniformType
        {
            /// <summary>
            /// Float 
            /// </summary>
            FLOAT, 
            /// <summary>
            /// Vec2 Float 
            /// </summary>
            FLOAT_VEC2,
            /// <summary>
            /// Vec3 Float
            /// </summary>
            FLOAT_VEC3,
            /// <summary>
            /// Vec4 Float
            /// </summary>
            FLOAT_VEC4, 
            /// <summary>
            /// Matrix 2x2 with Floats
            /// </summary>
            FLOAT_MAT2, 
            /// <summary>
            /// Matrix 3x3 with Floats
            /// </summary>
            FLOAT_MAT3, 
            /// <summary>
            /// Matrix 4x4 with Floats
            /// </summary>
            FLOAT_MAT4,
            /// <summary>
            /// Integer
            /// </summary>
            INT,
            /// <summary>
            /// Vec2 with Ints
            /// </summary>
            INT_VEC2,
            /// <summary>
            /// Vec3 with Ints
            /// </summary>
            INT_VEC3,
            /// <summary>
            /// Vec4 with Ints
            /// </summary>
            INT_VEC4,
            /// <summary>
            /// Bool
            /// </summary>
            BOOL,
            /// <summary>
            /// Vec2 with Bools
            /// </summary>
            BOOL_VEC2,
            /// <summary>
            /// Vec3 with Bools
            /// </summary>
            BOOL_VEC3,
            /// <summary>
            /// Vec4 with Bools
            /// </summary>
            BOOL_VEC4,
            /// <summary>
            /// 1D Sampler
            /// </summary>
            SAMPLER_1D,
            /// <summary>
            /// 2D Sampler
            /// </summary>
            SAMPLER_2D,
            /// <summary>
            /// 3D Sampler
            /// </summary>
            SAMPLER_3D, 
            /// <summary>
            /// Sampler Cube
            /// </summary>
            SAMPLER_CUBE,
            /// <summary>
            /// 1D Shadow Sampler
            /// </summary>
            SAMPLER_1D_SHADOW,
            /// <summary>
            /// 2D Shadow Sampler
            /// </summary>
            SAMPLER_2D_SHADOW
        }
        /// <summary>
        /// Enumerates the different cases where the variable is found in
        /// </summary>
        public enum FoundInRoutine
        {
            /// <summary>
            /// Vertex program only
            /// </summary>
            VERTEX,
            /// <summary>
            /// Fragement porgram only
            /// </summary>
            FRAGMENT, 
            /// <summary>
            /// Both Vertex and Fragment Program
            /// </summary>
            BOTH, 
            /// <summary>
            /// Don't care about it
            /// </summary>
            DONT_CARE
        }

        private int size;
        private UniformType type;
        private FoundInRoutine foundIn;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Size">size</param>
        /// <param name="Type">uniform type</param>
        /// <param name="FoundIn">Routine the variable is found in</param>
        public UniformVariableInfo(int Size, UniformType Type, FoundInRoutine FoundIn)
        {
            size = Size;
            type = Type;
            foundIn = FoundIn;
        }

        #region Public Properties

        /// <summary>
        /// Get / Set the size of the variable
        /// </summary>
        public int Size
        {
            get { return size; }
            set { size = value; }
        }

        /// <summary>
        /// Get / Set the variable type
        /// </summary>
        public UniformType Type
        {
            get { return type; }
            set { type = value; }
        }

        /// <summary>
        /// Get/Set the routine in which the variable is found
        /// </summary>
        public FoundInRoutine FoundIn
        {
            get { return foundIn; }
            set { foundIn = value; }
        }

        #endregion

        #region Operators
        /// <summary>
        /// Equality operator
        /// </summary>
        /// <param name="a">variable in focus </param>
        /// <param name="b">check against</param>
        /// <returns>true or false</returns>
        public static bool operator ==(UniformVariableInfo a, UniformVariableInfo b)
        {
            if (a.Size != b.Size) return false;
            if (a.Type != b.Type) return false;
            if (a.FoundIn != b.FoundIn) return false;
            return true;
        }
        /// <summary>
        /// Not Equal to Operator
        /// </summary>
        /// <param name="a">variable in focus</param>
        /// <param name="b">check against</param>
        /// <returns>true or false</returns>
        public static bool operator !=(UniformVariableInfo a, UniformVariableInfo b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Equality operator
        /// </summary>
        /// <param name="obj">input object</param>
        /// <returns></returns>
        public override bool Equals(Object obj)
        {
            return (obj is UniformVariableInfo) && (this == (UniformVariableInfo)obj);
        }

        /// <summary>
        /// Gets the hash code 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return type.GetHashCode();
        }

        #endregion

        #region public static method
        
        
        /// <summary>
        /// static method to check if a sampler variable is used in Vertex, Fragment, or Both
        /// </summary>
        /// <param name="foundIn">found in routine</param>
        /// <param name="usedInVertex">check if used in vertex program</param>
        /// <param name="usedInFragment">check if used in fragment program</param>
        public static void FindWhereSamplerUsed(UniformVariableInfo.FoundInRoutine foundIn, 
            out bool usedInVertex, out bool usedInFragment)
        {
            usedInVertex = false;
            usedInFragment = false;
            if (foundIn == UniformVariableInfo.FoundInRoutine.BOTH)
            {
                usedInVertex = true;
                usedInFragment = true;
            }
            else if (foundIn == UniformVariableInfo.FoundInRoutine.VERTEX)
            {
                usedInVertex = true;
            }
            else if (foundIn == UniformVariableInfo.FoundInRoutine.FRAGMENT)
            {
                usedInFragment = true;
            }
        }
        #endregion
    }

    /// <summary>
    /// A container class for storing the uniform variable values, which is a shared asset
    /// </summary>
    [Serializable()]
    public class UniformVariableValueContainer : SharedAsset
    {
        /// <summary>
        /// Store a bool
        /// </summary>
        public bool Bool;
        //public int Int;
        /// <summary>
        /// Store a vector
        /// </summary>
        public IVector Vec = new Vector4();
        /// <summary>
        /// Store a matrix
        /// </summary>
        public Matrix Mat = new Matrix4();
        /// <summary>
        /// Store a shared float
        /// </summary>
        private SharedFloat _float = new SharedFloat(0);
        /// <summary>
        /// Get/Set float variables
        /// </summary>
        public SharedFloat Float
        {
            get { return _float; }
            set
            {
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(float_ChangedEvent);
                this.SetPropertyEventHandling(_float, value, changedEvent);
                _float = value;
                float_ChangedEvent(_float);
            }
        }
        private SharedInt _int;
        /// <summary>
        /// Get/Set Int variables
        /// </summary>
        public SharedInt Int
        {
            get { return _int; }
            set
            {
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(int_ChangedEvent);
                this.SetPropertyEventHandling(_int, value, changedEvent);
                _int = value;
                int_ChangedEvent(_int);
            }
        }
        
        private TextureProxy _sampler;
        /// <summary>
        /// Get/Set a sampler
        /// </summary>
        public TextureProxy Sampler
        {
            get { return _sampler; }
            set
            {
                //
                // For shared objects, follow this sequence:
                //   1. rewire the notification for when the object
                //        this property points to changes.
                //   2. set the property.
                //   3. perform any internal updates.
                //   4. Implicit in step 3 would be changing this 
                //         object and notifying its subscribers.
                IAssetManagedHandler changedEvent = new IAssetManagedHandler(sampler_ChangedEvent);
                this.SetPropertyEventHandling(_sampler, value, changedEvent);
                _sampler = value;
                sampler_ChangedEvent(_sampler);
            }
        }

        /// <summary>
        /// Fire an event if a sampler changed
        /// </summary>
        /// <param name="obj">object</param>
        protected void sampler_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }

        /// <summary>
        /// Fire an event if a float changed
        /// </summary>
        /// <param name="obj">object</param>
        protected void float_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }

        /// <summary>
        /// Fire an event if an integer changed
        /// </summary>
        /// <param name="obj">integer</param>
        protected void int_ChangedEvent(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }

        /// <summary>
        /// Constructor for initializing the different variable types
        /// </summary>
        public UniformVariableValueContainer()
            : base()
        {
            Bool = false;
            //Int = 0;
            Int = new SharedInt(0);
            //Float = 0.0f;
            Float = new SharedFloat(0.0f);
            Vec = new Vector();//null;
            Mat = new Matrix2();// null;
            Sampler = null;
        }
        /// <summary>
        /// Implementation of SharedAsset ReacquireAssetMethod
        /// </summary>
        public override void ReaquireAssets()
        {
            //
            // Use the Properties to ensure the old event handlers are removed and new
            //    ones are added. Particularly if we are setting an item to itself.
            //
            if (Sampler != null)
            {
                IAssetManaged item = AssetManager.Instance.Find(_sampler.GetType(), _sampler.Name);
                if (item != _sampler)
                {
                    this.Sampler = item as TextureProxy;
                }
            }
        }
        private void OnDeserialized( StreamingContext context )
        {
            IAssetManagedHandler changedEvent = new IAssetManagedHandler(sampler_ChangedEvent);
            IAssetManaged asset = _sampler as IAssetManaged;
            if (asset != null)
            {
                asset.AddChangedEventHandler(changedEvent);
            }
            asset = _float as IAssetManaged;
            if (asset != null)
            {
                asset.AddChangedEventHandler(changedEvent);
            }
        }

    }
    
    /*//now in separate file
    [Serializable()]
    public class SharedFloat : SharedObject
    {
        private float float_value;        
        public SharedFloat(float v)
        {
            float_value = v;
        }
        public SharedFloat(String name) : base(name)
        {            
        }
        public SharedFloat(String name, float v) : base(name)
        {
            float_value = v;
        }
        public float Float_Value
        {
            get { return float_value; }
            set { float_value = value; }
        }
    }
     */

    //
    // Uniform Variable Values - container that stores the UVInfo and its value
    //

    /// <summary>
    /// UniformVariableValue class - a container that stores the UVInfo and its value
    /// </summary>
    [Serializable()]
    public class UniformVariableValues : SharedAsset
    {
        //
        // The information about the Uniform Variable and it's value
        //
        private UniformVariableInfo uvInfo;
        private List<UniformVariableValueContainer> val;

        //
        // Events and delegates for UniformVariableValues
        //
        //public delegate void UVVHandler(UniformVariableValues vals);

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="info">uniform variable information</param>
        public UniformVariableValues(UniformVariableInfo info) : base()
        {
            uvInfo = info;

            //
            // Initialize values list
            //
            val = new List<UniformVariableValueContainer>();
            for (int i = 0; i < uvInfo.Size; i++)
            {
                UniformVariableValueContainer item = new UniformVariableValueContainer();
                item.AddChangedEventHandler(new IAssetManagedHandler(uvvc_MemberChanged));
                val.Add(item);
            }

        }

        #region Public Properties

        /// <summary>
        /// Set UniformVariableInfo
        /// </summary>
        public UniformVariableInfo UVInfo
        {
            set
            {
                //
                // Be careful if the info changes we want to recreate the values.  This is expecially true
                //  if the size were to change.
                //
                if (uvInfo != value)
                {
                    //
                    // TODO: I believe this causes a memory leak. The items in the list should remove their 
                    //     event delegate lists (Roger).
                    //
                    val.Clear();
                    for (int i = 0; i < value.Size; i++)
                    {
                        UniformVariableValueContainer item = new UniformVariableValueContainer();
                        item.AddChangedEventHandler(uvvc_MemberChanged);
                        val.Add(item);
                    }
                }
                uvInfo = value;
            }
        }

        /// <summary>
        /// Get size of UniformVariableInfo
        /// </summary>
        public int Size
        {
            get { return uvInfo.Size; }
        }

        /// <summary>
        /// Get the UVInfo type
        /// </summary>
        public UniformVariableInfo.UniformType Type
        {
            get { return uvInfo.Type; }
        }

        /// <summary>
        /// Get the routine in which the UVInfo is found
        /// </summary>
        public UniformVariableInfo.FoundInRoutine FoundIn
        {
            get { return uvInfo.FoundIn; }
        }

        /// <summary>
        /// Get/Set the UVV Container
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public UniformVariableValueContainer this[int index]
        {
            get { return val[index]; }
            set { val[index] = value; }
        }

        #endregion

        /// <summary>
        /// Fire an event if a uniform variale value container changes
        /// </summary>
        /// <param name="obj"></param>
        public void uvvc_MemberChanged(IAssetManaged obj)
        {
            FireUpdateChangedEvent(this);
        }
        /// <summary>
        /// Implementation of SharedAsset ReacquireAssets method
        /// </summary>
        public override void ReaquireAssets()
        {
            IAssetManagedHandler changedEvent = new IAssetManagedHandler(uvvc_MemberChanged);
            List<UniformVariableValueContainer> newValues = new List<UniformVariableValueContainer>(val.Count);
            for( int i=0; i < val.Count; i++ )
            {
                UniformVariableValueContainer item = val[i];
                IAssetManaged asset = AssetManager.Instance.Find(item.GetType(), item.Name);
                if (item != asset)
                {
                    item.RemoveChangedEventHandler(changedEvent);
                    asset.AddChangedEventHandler(changedEvent);
                    val[i] = asset as UniformVariableValueContainer;
                }
            }
        }
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            IAssetManagedHandler changedEvent = new IAssetManagedHandler(uvvc_MemberChanged);
            foreach( UniformVariableValueContainer item in val)
            {
                item.AddChangedEventHandler(changedEvent);
            }
        }
    }
}
